/*******************************************************************************
 * Copyright (c) 2000, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.ui.dialogs;

import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.osgi.util.NLS;

/**
 * For creating folder resources that currently do not exist, 
 * along a given workspace path.
 * <p>
 * This class may be instantiated; it is not intended to be subclassed.
 * </p>
 * <p>
 * Example usage:
 * <pre>
 * ContainerGenerator gen = new ContainerGenerator(new Path("/A/B"));
 * IContainer res = null;
 * try {
 *   res = gen.getContainer(monitor); // creates project A and folder B if required
 * } catch (CoreException e) {
 *   // handle failure
 * } catch (OperationCanceledException e) {
 *   // handle cancelation
 * }
 * </pre>
 * </p>
 * @noextend This class is not intended to be subclassed by clients.
 */
public class ContainerGenerator {
    private IPath containerFullPath;

    private IContainer container;

    /**
     * Creates a generator for the container resource (folder or project) at the
     * given workspace path. Assumes the path has already been validated.
     * <p>
     * Call <code>getContainer</code> to create any missing resources along the
     * path.
     * </p>
     *
     * @param containerPath the workspace path of the container
     */
    public ContainerGenerator(IPath containerPath) {
        super();
        this.containerFullPath = containerPath;
    }

    /**
     * Creates a folder resource for the given folder handle.
     *
     * @param folderHandle the handle to create a folder resource
     * @param monitor the progress monitor to show visual progress
     * @return the folder handle (<code>folderHandle</code>)
     * @exception CoreException if the operation fails
     * @exception OperationCanceledException if the operation is canceled
     */
    private IFolder createFolder(IFolder folderHandle, IProgressMonitor monitor)
            throws CoreException {
        folderHandle.create(false, true, monitor);

        if (monitor.isCanceled()) {
            throw new OperationCanceledException();
        }

        return folderHandle;
    }

    /**
     * Creates a folder resource handle for the folder with the given name.
     * This method does not create the folder resource; this is the responsibility
     * of <code>createFolder</code>.
     *
     * @param container the resource container
     * @param folderName the name of the folder
     * @return the new folder resource handle
     */
    private IFolder createFolderHandle(IContainer container, String folderName) {
        return container.getFolder(new Path(folderName));
    }

    /**
     * Creates a project resource for the given project handle.
     *
     * @param projectHandle the handle to create a project resource
     * @param monitor the progress monitor to show visual progress
     * @return the project handle (<code>projectHandle</code>)
     * @exception CoreException if the operation fails
     * @exception OperationCanceledException if the operation is canceled
     */
    private IProject createProject(IProject projectHandle,
                                   IProgressMonitor monitor) throws CoreException {
        try {
            monitor.beginTask("", 2000);//$NON-NLS-1$

            projectHandle.create(new SubProgressMonitor(monitor, 1000));
            if (monitor.isCanceled()) {
                throw new OperationCanceledException();
            }

            projectHandle.open(new SubProgressMonitor(monitor, 1000));
            if (monitor.isCanceled()) {
                throw new OperationCanceledException();
            }
        } finally {
            monitor.done();
        }

        return projectHandle;
    }

    /**
     * Creates a project resource handle for the project with the given name.
     * This method does not create the project resource; this is the responsibility
     * of <code>createProject</code>.
     *
     * @param root the workspace root resource
     * @param projectName the name of the project
     * @return the new project resource handle
     */
    private IProject createProjectHandle(IWorkspaceRoot root, String projectName) {
        return root.getProject(projectName);
    }

    /**
     * Ensures that this generator's container resource exists. Creates any missing
     * resource containers along the path; does nothing if the container resource
     * already exists.
     * <p>
     * Note: This method should be called within a workspace modify operation since
     * it may create resources.
     * </p>
     *
     * @param monitor a progress monitor
     * @return the container resource
     * @exception CoreException if the operation fails
     * @exception OperationCanceledException if the operation is canceled
     */
    public IContainer generateContainer(IProgressMonitor monitor)
            throws CoreException {
        ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
            public void run(IProgressMonitor monitor) throws CoreException {
                monitor
                        .beginTask(
                                "IDEWorkbenchMessages.ContainerGenerator_progressMessage", 1000 * containerFullPath.segmentCount());
                if (container != null) {
                    return;
                }

                // Does the container exist already?
                IWorkspaceRoot root = getWorkspaceRoot();
                container = (IContainer)root.findMember(containerFullPath);
                if (container != null) {
                    return;
                }

                // Create the container for the given path
                container = root;
                for (int i = 0; i < containerFullPath.segmentCount(); i++) {
                    String currentSegment = containerFullPath.segment(i);
                    IResource resource = container.findMember(currentSegment);
                    if (resource != null) {
                        if (resource.getType() == IResource.FILE) {
                            String msg =
                                    NLS.bind("IDEWorkbenchMessages.ContainerGenerator_pathOccupied", resource.getFullPath().makeRelative());
                            throw new CoreException(new Status(IStatus.ERROR, "IDEWorkbenchPlugin.IDE_WORKBENCH", 1, msg, null));
                        }
                        container = (IContainer)resource;
                        monitor.worked(1000);
                    } else {
                        if (i == 0) {
                            IProject projectHandle = createProjectHandle(root,
                                                                         currentSegment);
                            container = createProject(projectHandle,
                                                      new SubProgressMonitor(monitor, 1000));
                        } else {
                            IFolder folderHandle = createFolderHandle(
                                    container, currentSegment);
                            container = createFolder(folderHandle,
                                                     new SubProgressMonitor(monitor, 1000));
                        }
                    }
                }
            }
        }, null, IResource.NONE, monitor);
        return container;
    }

    /**
     * Returns the workspace root resource handle.
     *
     * @return the workspace root resource handle
     */
    private IWorkspaceRoot getWorkspaceRoot() {
        return ResourcesPlugin.getWorkspace().getRoot();
    }
}
